<!DOCTYPE html>
<html lang="en">

<head>
  <title>cesium</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no">
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <!-- cdn连接 -->
  <link href="https://cesium.com/downloads/cesiumjs/releases/1.92/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
  <style>
    .cesium-viewer-bottom {
      display: none;
    }

    #cesiumContainer,
    html,
    body {
      padding: 0;
      margin: 0;
      width: 100%;
      height: 100%;
      background: #000;
    }


    .loading {
      width: 100%;
      height: 100%;
      position: fixed;
      background: transparent;
      top: 0;
      left: 0;
      animation: rotate 0.8s linear infinite;
    }

    @keyframes rotate {
      0% {
        transform: rotate(0deg);
      }

      100% {
        transform: rotate(360deg)
      }
    }

    .loading img {
      width: 30px;
      height: 30px;
      transform: translate(-50%, -50%);
      top: 50%;
      left: 50%;
      position: absolute;
      opacity: 0.7;
    }

    .stop {
      position: fixed;
      top: 20px;
      left: 20px;
      width: 45px;
      height: 45px;
      border-radius: 50%;
      background: #857677;
      cursor: pointer;
      display: none;
    }

    .stop span {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      color: rebeccapurple;
      white-space: nowrap;
      font-size: 12px;
    }
  </style>
</head>

<body>
  <div id="cesiumContainer"></div>
  <div class="loading">
    <img src="http://182.43.179.137:81/public/images/loading2.png" alt="">
  </div>
  <div class="stop">
    <span>暂停</span>
  </div>
</body>

<script src="http://182.43.179.137:81/public/cesium/cesium-1.92/Build/Cesium/Cesium.js"></script>
<script>

  //创建查看器
  const viewer = new Cesium.Viewer('cesiumContainer', {
    terrainProvider: Cesium.createWorldTerrain(), // 使用地形地图
    shouldAnimate: true, // 允许执行动画
    navigationHelpButton: false,
  });





  // 添加方向光源
  const vector = new Cesium.Cartesian3.fromDegrees(118, 30, 0)// 向量（世界坐标系原点，到地球表面东亚区域的某个点，对应的向量）
  viewer.scene.light = new Cesium.DirectionalLight({ //去除时间原因影响模型颜色
    direction: new Cesium.Cartesian3(-vector.x, -vector.y, -vector.z) // 光源方向向量
  })

  // x轴辅助线
  viewer.entities.add(
    new Cesium.Entity({ // 折线
      polyline: new Cesium.PolylineGraphics({
        positions: [new Cesium.Cartesian3(10, 0, 0), new Cesium.Cartesian3(9000000, 0, 0),],
        width: 2,
        material: new Cesium.PolylineArrowMaterialProperty(new Cesium.Color(255, 0, 0, 1)) // 材质
      })
    })
  )

  // y轴辅助线
  viewer.entities.add(
    new Cesium.Entity({// 折线
      polyline: new Cesium.PolylineGraphics({
        positions: [new Cesium.Cartesian3(0, 10, 0), new Cesium.Cartesian3(0, 9000000, 0),],
        width: 2,
        material: new Cesium.PolylineArrowMaterialProperty(new Cesium.Color(0, 255, 0, 1)) // 材质
      })
    })
  )

  // z轴辅助线
  viewer.entities.add(
    new Cesium.Entity({// 折线
      polyline: new Cesium.PolylineGraphics({// 折线
        positions: [new Cesium.Cartesian3(0, 0, 10), new Cesium.Cartesian3(0, 0, 9000000),],
        width: 2,
        material: new Cesium.PolylineArrowMaterialProperty(new Cesium.Color(0, 0, 255, 1)) // 材质
      })
    })
  )


  // 计算标准姿态对应的变换四元数orientation
  function calcStandardOrientation(position) {
    if (!position instanceof Cesium.Cartesian3) {
      throw new Error('calcStandardOrientation()参数错误，标准Cartesian3')
    }

    let x, y, z
    if (position.x == 0 && position.y == 0) {
      if (position.z >= 0) {
        x = new Cesium.Cartesian3(1, 0, 0)
        y = new Cesium.Cartesian3(0, 1, 0)
        z = new Cesium.Cartesian3(0, 0, 1)
      } else {
        x = new Cesium.Cartesian3(-1, 0, 0)
        y = new Cesium.Cartesian3(0, 1, 0)
        z = new Cesium.Cartesian3(0, 0, -1)
      }

    } else {

      z = Cesium.Cartesian3.normalize(position, new Cesium.Cartesian3()) // 归一化

      // 北极点
      const N = Cesium.Cartesian3.fromDegrees(0, 90, 0)
      // PN向量
      const PN = new Cesium.Cartesian3(N.x - position.x, N.y - position.y, N.z - position.z)
      // 计算PN和z的法向量
      y = new Cesium.Cartesian3((PN.y * z.z - PN.z * z.y), (PN.z * z.x - PN.x * z.z), (PN.x * z.y - PN.y * z.x)) // 根据两向量的向量积公式，参考数学相关笔记
      y = Cesium.Cartesian3.normalize(y, new Cesium.Cartesian3()) // 归一化

      // 计算y和z的法向量
      x = new Cesium.Cartesian3((y.y * z.z - y.z * z.y), (y.z * z.x - y.x * z.z), (y.x * z.y - y.y * z.x))
      x = Cesium.Cartesian3.normalize(x, new Cesium.Cartesian3()) // 归一化
    }

    // 构建旋转矩阵，并转换为四元数
    let matrix = new Cesium.Matrix3(x.x, y.x, z.x, x.y, y.y, z.y, x.z, y.z, z.z)
    let orientation = Cesium.Quaternion.fromRotationMatrix(matrix)
    return orientation
  }

  let loaded = false
  let start = true
  const bt = document.querySelector('.stop')
  bt && bt.addEventListener('click', () => {
    start = !start
    const el = document.querySelector('.stop span')
    if (!el) return;
    if (start) {
      el.innerHTML = '暂停'
    } else {
      el.innerHTML = '开启'

    }
  })
  // 给飞机施加标准姿态下的偏航。使飞机平行地面，朝向航线飞线
  let hpr = Cesium.Quaternion.fromHeadingPitchRoll(new Cesium.HeadingPitchRoll(
    Cesium.Math.toRadians(90),
    Cesium.Math.toRadians(0),
    Cesium.Math.toRadians(0),
  ))

  // 飞机位置，经纬度，高度
  const position = [118, 35, 100000]

  // 创建飞机
  let airplane = new Cesium.Entity({
    position: new Cesium.Cartesian3.fromDegrees(...position),
    orientation: Cesium.Quaternion.multiply(calcStandardOrientation(new Cesium.Cartesian3.fromDegrees(...position)), hpr, new Cesium.Quaternion()),
    model: new Cesium.ModelGraphics({
      show: false,
      uri: 'http://182.43.179.137:81/public/3d-models/Cesium_Air.glb',
      scale: 18000,
      minimumPixelSize: 8,
      maximumScale: 30000,
      runAnimations: true, // 启动模型的glTF动画
      clampAnimations: true,
    })
  })

  // 添加和跟踪
  viewer.entities.add(airplane)

  // 飞机1位置，经纬度，高度
  const position1 = [127, 38, 200000]


  // 创建飞机1
  let airplane1 = new Cesium.Entity({
    position: new Cesium.Cartesian3.fromDegrees(...position1),
    orientation: Cesium.Quaternion.multiply(calcStandardOrientation(new Cesium.Cartesian3.fromDegrees(...position1)), hpr, new Cesium.Quaternion()),
    model: new Cesium.ModelGraphics({
      show: false,
      uri: 'http://182.43.179.137:81/public/3d-models/Cesium_Air.glb',
      scale: 18000,
      minimumPixelSize: 8,
      maximumScale: 30000,
      runAnimations: true, // 启动模型的glTF动画
      clampAnimations: true,
    })
  })

  // 添加和跟踪
  viewer.entities.add(airplane1)
  //viewer.trackedEntity = airplane



  // 飞机2位置，经纬度，高度
  const position2 = [129 + 6, 32, 300000]


  // 创建飞机2
  let airplane2 = new Cesium.Entity({
    position: new Cesium.Cartesian3.fromDegrees(...position2),
    orientation: Cesium.Quaternion.multiply(calcStandardOrientation(new Cesium.Cartesian3.fromDegrees(...position2)), hpr, new Cesium.Quaternion()),
    model: new Cesium.ModelGraphics({
      show: false,
      uri: 'http://182.43.179.137:81/public/3d-models/Cesium_Air.glb',
      scale: 18000,
      minimumPixelSize: 8,
      maximumScale: 30000,
      runAnimations: true, // 启动模型的glTF动画
      clampAnimations: true,
    })
  })

  // 添加和跟踪
  viewer.entities.add(airplane2)
  //viewer.trackedEntity = airplane



  // 飞机3位置，经纬度，高度
  const position3 = [129, 32, 300000]
  // 创建飞机3
  let airplane3 = new Cesium.Entity({
    position: new Cesium.Cartesian3.fromDegrees(...position3),
    orientation: Cesium.Quaternion.multiply(calcStandardOrientation(new Cesium.Cartesian3.fromDegrees(...position3)), hpr, new Cesium.Quaternion()),
    model: new Cesium.ModelGraphics({
      show: false,
      uri: 'http://182.43.179.137:81/public/3d-models/Cesium_Air.glb',
      scale: 18000,
      minimumPixelSize: 8,
      maximumScale: 30000,
      runAnimations: true, // 启动模型的glTF动画
      clampAnimations: true,
    })
  })

  // 添加和跟踪
  viewer.entities.add(airplane3)
  //viewer.trackedEntity = airplane

  // 设置相机
  viewer.camera.flyTo({
    // destination: Cesium.Cartesian3.fromDegrees(position[0] - 15, position[1] - 23, 13500000), // 相机观察位置
    destination: Cesium.Cartesian3.fromDegrees(position[0] - 20, position[1] - 1, position[2] + 1200000), // 相机观察位置
    orientation: {
      heading: Cesium.Math.toRadians(80.0),
      pitch: Cesium.Math.toRadians(-40.0),
      roll: Cesium.Math.toRadians(0.0),
      duration: 2,
    },
    complete: function () {

    } // flyTo()完成后回调
  });


  var helper = new Cesium.EventHelper();
  helper.add(viewer.scene.globe.tileLoadProgressEvent, function (e) {

    if (e == 0 && !loaded) {

      loaded = true
      airplane.model.show = true
      airplane1.model.show = true
      //airplane2.model.show = true
      airplane3.model.show = true
      const el = document.querySelector('.loading')
      el && (el.style.display = 'none')
      const el1 = document.querySelector('.stop')
      el1 && (el1.style.display = 'block')
      console.log("矢量切片加载完成时的回调");

      setTimeout(() => {
        // 设置相机
        viewer.camera.flyTo({
          destination: Cesium.Cartesian3.fromDegrees(position[0] - 15, position[1] - 23, 13500000), // 相机观察位置
          // destination: Cesium.Cartesian3.fromDegrees(position[0], position[1], 335000000), // 相机观察位置
          orientation: {
            heading: Cesium.Math.toRadians(-0.0),
            pitch: Cesium.Math.toRadians(-90.0),
            roll: Cesium.Math.toRadians(0.0),
            duration: 4,
          },
          complete: function () {


          } // flyTo()完成后回调
        });

        setTimeout(() => {
          setInterval(() => {
            if (!start) return;
            position[0] -= 1
            if (position[0] < -180) {
              position[0] = 180 - Math.abs(position[0] - (-180))
            }

            // 数据更新
            airplane.position = new Cesium.CallbackProperty(function () {
              return new Cesium.Cartesian3.fromDegrees(...position)
            })

            // 数据更新
            airplane.orientation = new Cesium.CallbackProperty(function () {
              return Cesium.Quaternion.multiply(calcStandardOrientation(new Cesium.Cartesian3.fromDegrees(...position)), hpr, new Cesium.Quaternion());
            })

            position1[0] -= 1
            if (position1[0] < -180) {
              position1[0] = 180 - Math.abs(position1[0] - (-180))
            }

            // 数据更新
            airplane1.position = new Cesium.CallbackProperty(function () {
              return new Cesium.Cartesian3.fromDegrees(...position1)
            })

            // 数据更新
            airplane1.orientation = new Cesium.CallbackProperty(function () {
              return Cesium.Quaternion.multiply(calcStandardOrientation(new Cesium.Cartesian3.fromDegrees(...position1)), hpr, new Cesium.Quaternion());
            })

            position2[0] -= 1
            if (position2[0] < -180) {
              position2[0] = 180 - Math.abs(position2[0] - (-180))
            }

            // 数据更新
            airplane2.position = new Cesium.CallbackProperty(function () {
              return new Cesium.Cartesian3.fromDegrees(...position2)
            })

            // 数据更新
            airplane2.orientation = new Cesium.CallbackProperty(function () {
              return Cesium.Quaternion.multiply(calcStandardOrientation(new Cesium.Cartesian3.fromDegrees(...position2)), hpr, new Cesium.Quaternion());
            })


            position3[0] -= 1
            if (position3[0] < -180) {
              position3[0] = 180 - Math.abs(position3[0] - (-180))
            }

            // 数据更新
            airplane3.position = new Cesium.CallbackProperty(function () {
              return new Cesium.Cartesian3.fromDegrees(...position3)
            })

            // 数据更新
            airplane3.orientation = new Cesium.CallbackProperty(function () {
              return Cesium.Quaternion.multiply(calcStandardOrientation(new Cesium.Cartesian3.fromDegrees(...position3)), hpr, new Cesium.Quaternion());
            })
          }, 50)

        }, 1500)
      }, 2000)

    }
  });

</script>

</html>